﻿/* AgileFx Version 1.0
 * An open-source framework for rapid development of applications and services using Microsoft.net
 * Developed by: AgileHead
 * Website: www.agilefx.org
 * This component is licensed under the terms of the Apache 2.0 License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Data.Objects.DataClasses;
using System.Reflection;

namespace AgileFx.EF.ModificationTypes
{
    public abstract class Modification<TContainer, TElement> : IModification<TElement>
        where TContainer : EntityObject, new()
        where TElement : EntityObject, new()
    {
        public FieldReference<TContainer, TElement> FieldReference { get; set; }

        #region IModification

        public override IFieldReference GetFieldReference()
        {
            return FieldReference;
        }

        public FieldReference<TContainer, TElement> GetTypedFieldReference()
        {
            return FieldReference;
        }

        #endregion

        #region Modification Methods

        public Modification<TContainer, TElement> Add<TField>(Expression<Func<TElement, TField>> fieldSelector,
            Action<TField> entityUpdate) where TField : EntityObject, new()
        {
            var upd = new EntityAddition<TElement, TField>(fieldSelector, entityUpdate);
            this.EntityFieldModifications.Add(upd);
            return this;
        }

        public Modification<TContainer, TElement> Add<TField>(Expression<Func<TElement, TField>> fieldSelector,
            Action<TField> entityUpdate, Action<EntityAddition<TElement, TField>> entityFieldUpdater)
            where TField : EntityObject, new()
        {
            var upd = new EntityAddition<TElement, TField>(fieldSelector, entityUpdate);
            entityFieldUpdater(upd);
            this.EntityFieldModifications.Add(upd);
            return this;
        }

        public Modification<TContainer, TElement> Add<TField>(Expression<Func<TElement, IEnumerable<TField>>> collectionSelector,
            params Action<TField>[] entityUpdate) where TField : EntityObject, new()
        {
            foreach (var func in entityUpdate)
            {
                this.EntityFieldModifications.Add(new EntityAddition<TElement, TField>(collectionSelector, func));
            }
            return this;
        }

        public Modification<TContainer, TElement> SetRef<TField>(Expression<Func<TElement, TField>> fieldSelector,
            Expression<Func<TField, bool>> predicate) where TField : EntityObject, new()
        {
            var upd = new SetReference<TElement, TField>(fieldSelector, predicate, null, null);
            this.EntityFieldModifications.Add(upd);
            return this;
        }

        public Modification<TContainer, TElement> SetRef<TField>(Expression<Func<TElement, IEnumerable<TField>>> fieldSelector,
            params Expression<Func<TField, bool>>[] predicates) where TField : EntityObject, new()
        {
            foreach (var func in predicates)
            {
                this.EntityFieldModifications.Add(new SetReference<TElement, TField>(fieldSelector, func, null));
            }
            return this;
        }

        public Modification<TContainer, TElement> SetRef<TField>(Expression<Func<TElement, TField>> fieldSelector,
            Expression<Func<TField, bool>> predicate, Action<TField> entityUpdate) where TField : EntityObject, new()
        {
            var upd = new SetReference<TElement, TField>(fieldSelector, predicate, entityUpdate, null);
            this.EntityFieldModifications.Add(upd);
            return this;
        }

        public Modification<TContainer, TElement> Update<TField>(Expression<Func<TElement, TField>> fieldSelector,
            Action<TField> entityUpdate) where TField : EntityObject, new()
        {
            var upd = new EntityUpdation<TElement, TField>(fieldSelector, entityUpdate, null);
            this.EntityFieldModifications.Add(upd);
            return this;
        }

        public Modification<TContainer, TElement> Update<TField>(Expression<Func<TElement, TField>> fieldSelector,
            Action<TField> entityUpdate, Action<EntityUpdation<TElement, TField>> entityFieldUpdater) 
            where TField : EntityObject, new()
        {
            var upd = new EntityUpdation<TElement, TField>(fieldSelector, entityUpdate, null);
            entityFieldUpdater(upd);
            this.EntityFieldModifications.Add(upd);
            return this;
        }

        public Modification<TContainer, TElement> Remove<TField>(Expression<Func<TElement, TField>> fieldSelector)
            where TField : EntityObject, new()
        {
            var upd = new EntityDeletion<TElement, TField>(fieldSelector);
            this.EntityFieldModifications.Add(upd);
            return this;
        }

        public Modification<TContainer, TElement> Remove<TField>(Expression<Func<TElement, IEnumerable<TField>>> collectionSelector,
            Func<TField, bool> predicate)
            where TField : EntityObject, new()
        {
            var upd = new EntityDeletion<TElement, TField>(collectionSelector, predicate);
            this.EntityFieldModifications.Add(upd);
            return this;
        }
        #endregion
    }
}
